﻿
Imports osi.root.lock
Imports osi.root.connector
Imports osi.root.delegates
Imports osi.root.formation

Namespace resolver
    Public Module _resolver
        Public Event registered(ByVal T As Type, ByVal resolver As _do(Of Object))
        Public Event erased(ByVal T As Type)
        Private ReadOnly creator As map(Of comparable_type, _do(Of Object)) = Nothing
        Private l As duallock
        Private _fixed As Boolean = False

        Sub New()
            creator = New map(Of comparable_type, _do(Of Object))()
        End Sub

        'test purpose only
        Public Function registered_event_count() As Int64
            Return attached_delegate_count(registeredEvent)
            'Return attached_event_count(GetType(_resolver), "registered")
        End Function

        Public Function erased_event_count() As Int64
            Return attached_delegate_count(erasedEvent)
            'Return attached_event_count(GetType(_resolver), "erased")
        End Function

        Private Function default_resolver(Of T)(ByVal i As T) As _do(Of T)
            Return Function() As T
                       Return i
                   End Function
        End Function

        Private Sub lock_reader(ByVal d As void)
            assert(Not d Is Nothing)
            If fixed() Then
                d()
            Else
                l.reader_locked(d)
            End If
        End Sub

        Public Sub fix()
            l.writer_locked(Sub() _fixed = True)
        End Sub

        Public Function fixed() As Boolean
            Return _fixed
        End Function

        Public Function size() As Int32
            Return creator.size()
        End Function

        Public Sub register(ByVal T As Type, ByVal i As Object)
            assert(Not T Is Nothing)
            assert(Not i Is Nothing)
            register(T, default_resolver(i))
        End Sub

        Public Sub register(Of T)(ByVal i As T)
            assert(Not i Is Nothing)
            register(Of T)(default_resolver(i))
        End Sub

        Public Sub register(ByVal T As Type, ByVal d As _do(Of Object))
            assert(Not T Is Nothing)
            assert(Not d Is Nothing)
            If fixed() Then
                assert(False, "should not register after fix() has been called")
            Else
                l.writer_locked(Sub() creator(New comparable_type(T)) = d)
                RaiseEvent registered(T, d)
            End If
        End Sub

        Private Function assert_create(Of T2)(ByVal T As Type, ByVal d As _do(Of T2)) As Object
            assert(Not T Is Nothing)
            assert(Not d Is Nothing)
            Dim rtn As Object = Nothing
            rtn = d()
            If rtn Is Nothing AndAlso Not T.IsValueType() Then
                assert(False, "failed to resolve ", T.FullName, " via ", invoke_method_name(d))
            End If
            Return rtn
        End Function

        Public Sub register(Of T)(ByVal d As _do(Of T))
            assert(Not d Is Nothing)
            Dim type As Type = Nothing
            type = GetType(T)
            register(type, Function() As Object
                               Return assert_create(type, d)
                           End Function)
        End Sub

        Public Function resolve(ByVal T As Type, ByRef o As Object) As Boolean
            assert(Not T Is Nothing)
            Dim create As _do(Of Object) = Nothing
            create = resolver(T)
            If create Is Nothing Then
                Return False
            Else
                o = assert_create(T, create)
                Return True
            End If
        End Function

        Public Function resolve(ByVal T As Type) As Object
            assert(Not T Is Nothing)
            Dim o As Object = Nothing
            assert(resolve(T, o), "type ", T.FullName(), " has not been registered.")
            Return o
        End Function

        Public Function resolve(Of T)(ByRef o As T) As Boolean
            Dim i As Object = Nothing
            Return resolve(GetType(T), i) AndAlso cast(Of T)(i, o)
        End Function

        Public Function resolve(Of T)() As T
            Return cast(Of T)(resolve(GetType(T)))
        End Function

        Private Function unlocked_has_resolver(ByVal T As comparable_type) As Boolean
            Return creator.find(T) <> creator.end()
        End Function

        Public Function has_resolver(ByVal T As Type) As Boolean
            assert(Not T Is Nothing)
            Dim rtn As Boolean
            lock_reader(Sub()
                            rtn = unlocked_has_resolver(New comparable_type(T))
                        End Sub)
            Return rtn
        End Function

        Public Function has_resolver(Of T)() As Boolean
            Return has_resolver(GetType(T))
        End Function

        Public Function resolver(ByVal T As Type) As _do(Of Object)
            assert(Not T Is Nothing)
            Dim rtn As _do(Of Object) = Nothing
            Dim ct As comparable_type = Nothing
            ct = New comparable_type(T)
            lock_reader(Sub()
                            If unlocked_has_resolver(ct) Then
                                rtn = creator(ct)
                            Else
                                rtn = Nothing
                            End If
                        End Sub)
            Return rtn
        End Function

        Public Function resolver(Of T)() As _do(Of Object)
            Return resolver(GetType(T))
        End Function

        Public Function [erase](ByVal T As Type) As Boolean
            If Not T Is Nothing AndAlso l.writer_locked(Function() creator.erase(New comparable_type(T))) Then
                RaiseEvent erased(T)
                Return True
            Else
                Return False
            End If
        End Function

        Public Function [erase](Of T)() As Boolean
            Return [erase](GetType(T))
        End Function

        Public Function resolve_or_alloc(Of T)() As T
            If has_resolver(Of T)() Then
                Return resolve(Of T)()
            Else
                Return alloc(Of T)()
            End If
        End Function

        Public Function check_resolve_or_alloc(Of T As Class)(ByVal i As T) As T
            If i Is Nothing Then
                Return resolve_or_alloc(Of T)()
            Else
                Return i
            End If
        End Function
    End Module
End Namespace
